package com.stary.pay.unionpay;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;
import com.stary.pay.unionpay.api.UnionpayApiException;
import com.stary.pay.unionpay.api.UnionpayClient;
import com.stary.pay.unionpay.api.UnionpayConstants;
import com.stary.pay.unionpay.api.request.UnionpayEncryptCertUpdateQueryRequest;
import com.stary.pay.unionpay.api.request.UnionpayFileDownloadBillRequest;
import com.stary.pay.unionpay.api.request.UnionpayOnlinePagePayRequest;
import com.stary.pay.unionpay.api.request.UnionpayOnlinePagePreAuthPayRequest;
import com.stary.pay.unionpay.api.request.UnionpayOnlinePreAuthFinishRequest;
import com.stary.pay.unionpay.api.request.UnionpayOnlinePreAuthFinishUndoRequest;
import com.stary.pay.unionpay.api.request.UnionpayOnlinePreAuthUndoRequest;
import com.stary.pay.unionpay.api.request.UnionpayOnlineQueryRequest;
import com.stary.pay.unionpay.api.request.UnionpayOnlineRefundRequest;
import com.stary.pay.unionpay.api.request.UnionpayOnlineUndoRequest;
import com.stary.pay.unionpay.api.response.UnionpayEncryptCertUpdateQueryResponse;
import com.stary.pay.unionpay.api.response.UnionpayFileDownloadBillResponse;
import com.stary.pay.unionpay.api.response.UnionpayOnlinePagePayResponse;
import com.stary.pay.unionpay.api.response.UnionpayOnlinePagePreAuthPayResponse;
import com.stary.pay.unionpay.api.response.UnionpayOnlinePreAuthFinishResponse;
import com.stary.pay.unionpay.api.response.UnionpayOnlinePreAuthFinishUndoResponse;
import com.stary.pay.unionpay.api.response.UnionpayOnlinePreAuthUndoResponse;
import com.stary.pay.unionpay.api.response.UnionpayOnlineQueryResponse;
import com.stary.pay.unionpay.api.response.UnionpayOnlineRefundResponse;
import com.stary.pay.unionpay.api.response.UnionpayOnlineUndoResponse;
import com.stary.pay.unionpay.api.util.StringUtils;
import com.stary.pay.unionpay.api.util.UnionpayHashMap;

/**
 * <p>银联支付相关业务</p>
 * @author stary {@link stary1993@qq.com}
 * @since 2019-7-5
 */
public class Unionpay {

	private static final Logger logger = LoggerFactory.getLogger(Unionpay.class);
	
	/**
	 * 请求客户端
	 */
	private UnionpayClient unionpayClient;

	public Unionpay() {
		super();
	}

	public Unionpay(UnionpayClient unionpayClient) {
		super();
		this.unionpayClient = unionpayClient;
	}

	public UnionpayClient getUnionpayClient() {
		return unionpayClient;
	}

	public void setUnionpayClient(UnionpayClient unionpayClient) {
		this.unionpayClient = unionpayClient;
	}

	/**
	 * <p>在线网关支付消费</p>
	 * 消费是指境内外持卡人在境内外商户网站进行购物等消费时用银行卡结算的交易，经批准的消费额将即时 地反映到该持卡人的账户余额上。
	 * @param orderId 商户订单号，自定义，不能含“-”或“_”，商户代码merId、商户订单号orderId、订单发送时间txnTime三要素唯一确定一笔交易。
	 * @param txnAmt 交易金额，单位为分。
	 * @param frontUrl 前台通知地址（可选），前台返回商户结果时使用，前台类交易需上送，不支持换行符等不可见字符。
	 * @param backUrl 后台通知地址（可选），如果不需要发后台通知，可以固定上送http://www.specialUrl.com。
	 * @param payTimeout 支付超时时间（可选），格式为YYYYMMDDhhmmss，超过此时间用户支付成功的交易，不通知商户，系统自动退款，大约5个工作日金额返还到用户账户。
	 * @param others 其他可选参数Map，特殊用法见官方文档 {@link https://open.unionpay.com/tjweb/acproduct/APIList?acpAPIId=275&&apiservId=448&&version=V2.2}。
	 * @return {@link UnionpayOnlinePagePayResponse}
	 */
	public UnionpayOnlinePagePayResponse onlinePagePay(String orderId, String txnAmt, String frontUrl, String backUrl, String payTimeout, UnionpayHashMap  others) {
		UnionpayOnlinePagePayResponse response = null;
		JSONObject bizContent = new JSONObject();
		bizContent.put(UnionpayConstants.ORDERID, orderId);
		bizContent.put(UnionpayConstants.TXNAMT, txnAmt);
		if (!StringUtils.isEmpty(payTimeout)) {
			bizContent.put(UnionpayConstants.PAYTIMEOUT, payTimeout);
		}
		if (others !=null) {
			for (Map.Entry<String, String> entry : others.entrySet()) {
				if (StringUtils.isEmpty(entry.getValue())) {
					continue;
				}
				bizContent.put(entry.getKey().trim(), entry.getValue());
			}
		}
		UnionpayOnlinePagePayRequest request = new UnionpayOnlinePagePayRequest();
		if (StringUtils.isEmpty(backUrl)) {
			backUrl = "http://www.specialUrl.com";
		}
		request.setFrontUrl(frontUrl);
		request.setBackUrl(backUrl);
		request.setBizContent(bizContent.toString());
		try {
			response = unionpayClient.pageExecute(request);
		} catch (UnionpayApiException e) {
			logger.error("onlinePagePay() error", e);
		}
		return response;
	}	
	/**
	 * <p>在线网关支付消费撤销</p>
	 * 是指因人为原因而撤销已完成的消费，消费撤销必须是撤销CUPS当日当批的消费。发卡行批准的消费撤销金额将即时地反映到该持卡人的账户上。完成交易的过程不需要同持卡人交互，属于后台交易。
	 * @param orderId 商户订单号，自定义，不能含“-”或“_”，商户代码merId、商户订单号orderId、订单发送时间txnTime三要素唯一确定一笔交易。
	 * @param origQryId 原始交易流水号，原消费交易返回的的queryId，可以从消费交易后台通知接口中或者交易状态查询接口中获取。
	 * @param txnAmt 撤销金额，消费撤销时必须和原消费金额相同。
	 * @param backUrl 后台通知地址，如果不需要发后台通知，可以固定上送http://www.specialUrl.com。
	 * @param others 其他可选参数Map，特殊用法见官方文档 {@link https://open.unionpay.com/tjweb/acproduct/APIList?acpAPIId=276&&apiservId=448&&version=V2.2}。
	 * @return {@link UnionpayOnlineUndoResponse}
	 */
	public UnionpayOnlineUndoResponse onlineUndo(String orderId, String origQryId, String txnAmt, String backUrl, UnionpayHashMap  others) {
		UnionpayOnlineUndoResponse response = null;
		JSONObject bizContent = new JSONObject();
		bizContent.put(UnionpayConstants.ORDERID, orderId);
		bizContent.put(UnionpayConstants.ORIGQRYID, origQryId);
		bizContent.put(UnionpayConstants.TXNAMT, txnAmt);
		if (others !=null) {
			for (Map.Entry<String, String> entry : others.entrySet()) {
				if(StringUtils.isEmpty(entry.getValue())) {
					continue;
				}
				bizContent.put(entry.getKey().trim(), entry.getValue());
			}
		}
		UnionpayOnlineUndoRequest request = new UnionpayOnlineUndoRequest();
		if (StringUtils.isEmpty(backUrl)) {
			backUrl = "http://www.specialUrl.com";
		}
		request.setBackUrl(backUrl);
		request.setBizContent(bizContent.toString());
		try {
			response = unionpayClient.execute(request);
		} catch (UnionpayApiException e) {
			logger.error("onlineUndo() error", e);
		}
		return response;
	}
	/**
	 * <p>在线网关支付退货</p>
	 * 对于跨清算日或者当清算日的消费交易，实现客户的退款需求，支持部分退货、多次退货。该交易参加资金清算,为后台交易。
	 * @param orderId 商户订单号，自定义，不能含“-”或“_”，商户代码merId、商户订单号orderId、订单发送时间txnTime三要素唯一确定一笔交易。
	 * @param origQryId 原始交易流水号，原消费交易返回的的queryId，可以从消费交易后台通知接口中或者交易状态查询接口中获取。
	 * @param txnAmt 退货金额，消费撤销时必须和原消费金额相同。
	 * @param backUrl 后台通知地址，如果不需要发后台通知，可以固定上送http://www.specialUrl.com。
	 * @param others 其他可选参数Map，特殊用法见官方文档 {@link https://open.unionpay.com/tjweb/acproduct/APIList?acpAPIId=276&&apiservId=448&&version=V2.2}。
	 * @return {@link UnionpayOnlineRefundResponse}
	 */
	public UnionpayOnlineRefundResponse onlineRefund(String orderId, String origQryId, String txnAmt, String backUrl, UnionpayHashMap  others) {
		UnionpayOnlineRefundResponse response = null;
		JSONObject bizContent = new JSONObject();
		bizContent.put(UnionpayConstants.ORDERID, orderId);
		bizContent.put(UnionpayConstants.ORIGQRYID, origQryId);
		bizContent.put(UnionpayConstants.TXNAMT, txnAmt);
		if (others !=null) {
			for (Map.Entry<String, String> entry : others.entrySet()) {
				if(StringUtils.isEmpty(entry.getValue())) {
					continue;
				}
				bizContent.put(entry.getKey().trim(), entry.getValue());
			}
		}
		UnionpayOnlineRefundRequest request = new UnionpayOnlineRefundRequest();
		if (StringUtils.isEmpty(backUrl)) {
			backUrl = "http://www.specialUrl.com";
		}
		request.setBackUrl(backUrl);
		request.setBizContent(bizContent.toString());
		try {
			response = unionpayClient.execute(request);
		} catch (UnionpayApiException e) {
			logger.error("onlineRefund() error", e);
		}
		return response;
	}
	/**
	 * <p>在线网关支付交易状态查询</p>
	 * 对于未收到交易结果的联机交易，商户应向银联全渠道支付平台发起交易状态查询交易，查询交易结果。完成交易的过程不需要同持卡人交互，属于后台交易。
	 * @param orderId 商户订单号
	 * @return {@link UnionpayOnlineQueryResponse}
	 */
	public UnionpayOnlineQueryResponse onlineQuery(String orderId) {
		UnionpayOnlineQueryResponse response = null;
		JSONObject bizContent = new JSONObject();
		bizContent.put(UnionpayConstants.ORDERID, orderId);
		UnionpayOnlineQueryRequest request = new UnionpayOnlineQueryRequest();
		request.setBizContent(bizContent.toString());
		try {
			response = unionpayClient.execute(request);
		} catch (UnionpayApiException e) {
			logger.error("onlineQuery() error", e);
		}
		return response;
	}
	/**
	 * <p>在线网关支付预授权</p>
	 * 预授权交易用于受理方向持卡人的发卡方确认交易许可。受理方将预估的消费金额作为预授权金额，发送给持卡人的发卡方。
	 * @param orderId 商户订单号，不能含“-”或“_”，商户代码merId、商户订单号orderId、订单发送时间txnTime三要素唯一确定一笔交易。
	 * @param txnAmt 交易金额，单位为分。
	 * @param frontUrl 前台通知地址（可选），前台返回商户结果时使用，前台类交易需上送，不支持换行符等不可见字符。
	 * @param backUrl 后台通知地址（可选），如果不需要发后台通知，可以固定上送http://www.specialUrl.com。
	 * @param payTimeout 支付超时时间（可选），格式为YYYYMMDDhhmmss，超过此时间用户支付成功的交易，不通知商户，系统自动退款，大约5个工作日金额返还到用户账户。
	 * @param others 其他可选参数Map，特殊用法见官方文档 {@link https://open.unionpay.com/tjweb/acproduct/APIList?acpAPIId=280&&apiservId=448&&version=V2.2}。
	 * @return
	 */
	public UnionpayOnlinePagePreAuthPayResponse onlinePagePreAuthPay(String orderId, String txnAmt, String frontUrl, String backUrl, String payTimeout, UnionpayHashMap  others) {
		UnionpayOnlinePagePreAuthPayResponse response = null;
		JSONObject bizContent = new JSONObject();
		bizContent.put(UnionpayConstants.ORDERID, orderId);
		bizContent.put(UnionpayConstants.TXNAMT, txnAmt);
		if (!StringUtils.isEmpty(payTimeout)) {
			bizContent.put(UnionpayConstants.PAYTIMEOUT, payTimeout);
		}
		if (others !=null) {
			for (Map.Entry<String, String> entry : others.entrySet()) {
				if(StringUtils.isEmpty(entry.getValue())) {
					continue;
				}
				bizContent.put(entry.getKey().trim(), entry.getValue());
			}
		}
		UnionpayOnlinePagePreAuthPayRequest request = new UnionpayOnlinePagePreAuthPayRequest();
		if (StringUtils.isEmpty(backUrl)) {
			backUrl = "http://www.specialUrl.com";
		}
		request.setFrontUrl(frontUrl);
		request.setBackUrl(backUrl);
		request.setBizContent(bizContent.toString());
		try {
			response = unionpayClient.pageExecute(request);
		} catch (UnionpayApiException e) {
			logger.error("onlinePagePreAuthPay() error", e);
		}
		return response;
	}
	/**
	 * <p>在线网关支付预授权撤销</p>
	 * 对已成功的POS预授权交易，在结算前使用预授权撤销交易，通知发卡方取消付款承诺。预授权撤销交易必须是对原始预授权交易或追加预授权交易最终承兑金额的全额撤销。
	 * @param orderId 商户订单号，自定义，不能含“-”或“_”，商户代码merId、商户订单号orderId、订单发送时间txnTime三要素唯一确定一笔交易。
	 * @param origQryId 原预授权的查询或通知接口的queryId字段
	 * @param txnAmt 原预授权金额
	 * @param backUrl 后台通知地址（可选），如果不需要发后台通知，可以固定上送http://www.specialUrl.com。
	 * @param others 其他可选参数Map，特殊用法见官方文档 {@link https://open.unionpay.com/tjweb/acproduct/APIList?acpAPIId=281&&apiservId=448&&version=V2.2}
	 * @return {@link UnionpayOnlinePreAuthUndoResponse}
	 */
	public UnionpayOnlinePreAuthUndoResponse onlinePreAuthUndo(String orderId, String origQryId, String txnAmt, String backUrl, UnionpayHashMap  others) {
		UnionpayOnlinePreAuthUndoResponse response = null;
		JSONObject bizContent = new JSONObject();
		bizContent.put(UnionpayConstants.ORDERID, orderId);
		bizContent.put(UnionpayConstants.ORIGQRYID, origQryId);
		bizContent.put(UnionpayConstants.TXNAMT, txnAmt);
		if (others !=null) {
			for (Map.Entry<String, String> entry : others.entrySet()) {
				if(StringUtils.isEmpty(entry.getValue())) {
					continue;
				}
				bizContent.put(entry.getKey().trim(), entry.getValue());
			}
		}
		UnionpayOnlinePreAuthUndoRequest request = new UnionpayOnlinePreAuthUndoRequest();
		if (StringUtils.isEmpty(backUrl)) {
			backUrl = "http://www.specialUrl.com";
		}
		request.setBackUrl(backUrl);
		request.setBizContent(bizContent.toString());
		try {
			response = unionpayClient.execute(request);
		} catch (UnionpayApiException e) {
			logger.error("onlinePreAuthUndo() error", e);
		}
		return response;
	}
	/**
	 * <p>在线网关支付预授权完成</p>
	 * 对已批准的预授权交易，用预授权完成做支付结算。
	 * @param orderId 商户订单号，自定义，不能含“-”或“_”，商户代码merId、商户订单号orderId、订单发送时间txnTime三要素唯一确定一笔交易。
	 * @param origQryId 原始预授权交易的queryId
	 * @param txnAmt 原预授权金额
	 * @param backUrl 后台通知地址（可选），如果不需要发后台通知，可以固定上送http://www.specialUrl.com。
	 * @param others 其他可选参数Map，特殊用法见官方文档 {@link https://open.unionpay.com/tjweb/acproduct/APIList?acpAPIId=282&&apiservId=448&&version=V2.2}
	 * @return {@link UnionpayOnlinePreAuthFinishResponse}
	 */
	public UnionpayOnlinePreAuthFinishResponse onlinePreAuthFinish(String orderId, String origQryId, String txnAmt, String backUrl, UnionpayHashMap  others) {
		UnionpayOnlinePreAuthFinishResponse response = null;
		JSONObject bizContent = new JSONObject();
		bizContent.put(UnionpayConstants.ORDERID, orderId);
		bizContent.put(UnionpayConstants.ORIGQRYID, origQryId);
		bizContent.put(UnionpayConstants.TXNAMT, txnAmt);
		if (others !=null) {
			for (Map.Entry<String, String> entry : others.entrySet()) {
				if(StringUtils.isEmpty(entry.getValue())) {
					continue;
				}
				bizContent.put(entry.getKey().trim(), entry.getValue());
			}
		}
		UnionpayOnlinePreAuthFinishRequest request = new UnionpayOnlinePreAuthFinishRequest();
		if (StringUtils.isEmpty(backUrl)) {
			backUrl = "http://www.specialUrl.com";
		}
		request.setBackUrl(backUrl);
		request.setBizContent(bizContent.toString());
		try {
			response = unionpayClient.execute(request);
		} catch (UnionpayApiException e) {
			logger.error("onlinePreAuthFinish() error", e);
		}
		return response;
	}
	/**
	 * <p>在线网关支付预授权完成撤销</p>
	 * 预授权完成撤销交易必须是对原始预授权完成交易的全额撤销。预授权完成撤销后的预授权仍然有效。
	 * @param orderId 商户订单号，自定义，不能含“-”或“_”，商户代码merId、商户订单号orderId、订单发送时间txnTime三要素唯一确定一笔交易。
	 * @param origQryId 原始预授权完成交易的queryId 
	 * @param txnAmt 原预授权完成金额
	 * @param backUrl 后台通知地址（可选），如果不需要发后台通知，可以固定上送http://www.specialUrl.com。
	 * @param others 其他可选参数Map，特殊用法见官方文档 {@link https://open.unionpay.com/tjweb/acproduct/APIList?acpAPIId=282&&apiservId=448&&version=V2.2}
	 * @return {@link UnionpayOnlinePreAuthFinishUndoResponse}
	 */
	public UnionpayOnlinePreAuthFinishUndoResponse onlinePreAuthFinishUndo(String orderId, String origQryId, String txnAmt, String backUrl, UnionpayHashMap  others) {
		UnionpayOnlinePreAuthFinishUndoResponse response = null;
		JSONObject bizContent = new JSONObject();
		bizContent.put(UnionpayConstants.ORDERID, orderId);
		bizContent.put(UnionpayConstants.ORIGQRYID, origQryId);
		bizContent.put(UnionpayConstants.TXNAMT, txnAmt);
		if (others !=null) {
			for (Map.Entry<String, String> entry : others.entrySet()) {
				if(StringUtils.isEmpty(entry.getValue())) {
					continue;
				}
				bizContent.put(entry.getKey().trim(), entry.getValue());
			}
		}
		UnionpayOnlinePreAuthFinishUndoRequest request = new UnionpayOnlinePreAuthFinishUndoRequest();
		if (StringUtils.isEmpty(backUrl)) {
			backUrl = "http://www.specialUrl.com";
		}
		request.setBackUrl(backUrl);
		request.setBizContent(bizContent.toString());
		try {
			response = unionpayClient.execute(request);
		} catch (UnionpayApiException e) {
			logger.error("onlinePreAuthFinishUndo() error", e);
		}
		return response;
	}
	/**
	 * <p>银联加密公钥更新查询</p>
	 * 商户定期（1天1次）向银联全渠道系统发起获取加密公钥信息交易。在加密公钥证书更新期间，全渠道系统支持新老证书的共同使用，新老证书并行期为1个月。全渠道系统向商户返回最新的加密公钥证书，由商户服务器替换本地证书。
	 * @param orderId 商户订单号，自定义，不能含“-”或“_”，商户代码merId、商户订单号orderId、订单发送时间txnTime三要素唯一确定一笔交易。
	 * @return {@link UnionpayEncryptCertUpdateQueryResponse}
	 */
	public UnionpayEncryptCertUpdateQueryResponse encryptCertUpdateQuery(String orderId) {
		UnionpayEncryptCertUpdateQueryResponse response = null;
		JSONObject bizContent = new JSONObject();
		bizContent.put(UnionpayConstants.ORDERID, orderId);
		UnionpayEncryptCertUpdateQueryRequest request = new UnionpayEncryptCertUpdateQueryRequest();
		request.setBizContent(bizContent.toString());
		try {
			response = unionpayClient.execute(request);
		} catch (UnionpayApiException e) {
			logger.error("encryptCertUpdateQuery() error", e);
		}
		return response;
	}
	/**
	 * <p>对账文件下载（文件传输）</p>
	 * @param settleDate 清算日期，格式 MMDD
	 * @return {@link UnionpayFileDownloadBillResponse}
	 */
	public UnionpayFileDownloadBillResponse fileDownloadBill(String settleDate) {
		UnionpayFileDownloadBillResponse response = null;
		JSONObject bizContent = new JSONObject();
		bizContent.put(UnionpayConstants.SETTLEDATE, settleDate);		
		bizContent.put(UnionpayConstants.FILETYPE, "00"); // 文件类型， 默认00 .zip
		UnionpayFileDownloadBillRequest request = new UnionpayFileDownloadBillRequest();
		request.setBizContent(bizContent.toString());
		try {
			response = unionpayClient.execute(request);
		} catch (UnionpayApiException e) {
			logger.error("fileDownloadBill() error", e);
		}
		return response;
	}

}
